\documentclass[12pt,a4paper]{article}
\usepackage{booktabs}
\usepackage{fontspec}
\usepackage{graphicx}
\usepackage{float}
\usepackage{subfigure}
\usepackage{geometry} %调整页面的页边距
\usepackage{pythonhighlight}
\geometry{left=2.5cm,right=2.5cm,top=2.5cm,bottom=2.5cm}%具体的页边距设置
\usepackage[UTF8]{ctex}
\title{销量预测\ 算法说明文档}
\author{AL\&DL302组}
\date{}
	
%————————————————————————————————————————————————————————————————————————————————
   
\begin{document}
	\maketitle 
        我们提供了三种模型算法，分别为Prophet时间序列模型、XGBoost决策树模型、LSTM长短记忆神经网络。前两种算法顺利输出预测结果，第三种算法由于设备计算能力有限，没有全部完成。\par
        Prophet方法的WMAPE值为0.776，XGBoost方法的WMAPE值为0.655。\par
        我们提供了三个预测结果，分别为$example1.csv$，$example2.csv$，$example3.csv$，其中$example1.csv$为prophet单模型预测结果，$example2.csv$为XGBoost单模型预测结果，$example3.csv$为上述两个结果的算数平均（在Excel上利用公式进行手动平均，这部分没有代码）。

    \section{基于$Prophet$模型的预测}
        文件名$prophet.py$，运行代码时，需要将main函数中的path改为实际的train和test文件的根目录，将output函数to\_csv中的路径修改为希望保存的路径

        \subsection*{数据处理}
        1、训练集商品ID不连续：从good\_ch文件中读取所有ID，然后用dataframe按ID取出所有数据\par 
        2、训练集日期不连续：划分训练集测试集后按时间进行排序再输入模型\par 
        3、训练集和测试集日期不连续：虑模型本身特性，只能预测在训练数据紧接着的日期往后periods时间，但是test的第一条和train的最后一条之间日期可能相差很大，所以采取预测test第一天与train最后一天之间的实际天数点方式，但是会出现大量的“无用”预测，会影响模型的预测精度\par 
        4、训练集极值离群点：将数据化为标准正态分布后去除3sigma以外的数据点\par 
        5、测试集日期缺失：这部分影响到prophet中periods的值，因为测试集中有日期缺失，所以数据条数并不等于预测天数，所以计算训练集第一天到最后一天的实际相差的天数作为periods

        \subsection*{本地训练时训练集和测试集的划分}
        考虑到离群值因素，先将原始数据（train中）划分为训练集：测试集=0.85:0.15的比例，然后在训练集中进行数据清洗，所以真实的训练集:测试集可能并达不到0.85:0.15，会影响预测结果。

        \subsection*{模型参数}
        由于运行程序的时间成本比较大，所以只进行了手工调参，没有采用参数寻优，采用的参数如下。
        \begin{python}
            interval_width=0.95,
            growth='linear',
            changepoint_range=0.9
            changepoint_prior_scale=0.008,
            holidays=holidays,
            holidays_prior_scale=0.05,
            n_changepoints=30
        \end{python}

        \subsection*{运行时间}
        单进程仅使用循环训练时，500个商品就用了40分钟，四进程运行需要1400秒大约23分钟

        \subsection*{评价函数得分}
        prophet预测的WMAPE=0.775529

        \subsection*{文件读取格式}
        1、考虑到sale\_date在csv中格式20210603与2021/6/3之间的区别，Prophet模型输出的result.csv文件中的日期是2021/6/3的格式，是后来手动在Excel上修改成了20210603的格式\par 
        2、程序实际运行输出的result文件中，由于dataframe的属性，会多一列index，需要在csv文件中手动删除
    
    \section{基于$XGBoost$模型的预测}
        文件名$xgb\_predict.py$，运行代码时，需装有xgboost、sklearn库，将main函数中的path参数改为train和test文件所在目录，将result函数中to\_csv的路径参数改为保存预测结果的路径。

        \subsection*{数据处理}
        数据预处理是算法的第一部分，涉及三个函数：分别是$preprocessing()$，$read\_data()$，$merge\_attr()$，下面对数据预处理过程进行简单介绍：\par

        1、$read\_data$函数读入初始数据，从csv文件中分别读入日期明细、商品明细、训练集、测试集数据，将部分列数据转为object属性，以便于后续进行离散数据的标准化。与此同时，将$official\_holiday\_name$，$festival\_name$，$season\_class\_name$，$div\_name$列删除，因为它们均有相应的id列数据代替。\par

        2、$merge\_attr$函数整合数据表，将商品明细、日期明细表的数据合并到训练集与测试集中，方便后续处理。\par

        3、$preprocessing$函数整合了整个数据预处理过程，引用了前两个函数，进行数据导入与数据表合并。这里再次对数据列进行了一次筛选：我们注意到商品明细表中有四级商品id信息，我们只取其中的$catg\_l\_id$列以简化模型。接下来进行数据的标准化，将连续型数据转化为服从标准正态分布的形式，离散型数值转成指示特征,按取值划分出更多属性。处理至此，测试集中的数据样式如下图所示。\par

        \begin{figure}[H]
            \centering
            \includegraphics[height=0.23\linewidth]{1.png}
            \caption{数据预处理完成时的数据样式}
        \end{figure}\par

        \subsection*{调参过程}
        将原训练集八二分为训练集和验证集，自定义WMAPE评价函数评价模型，调整xgboost的各种超参数。在调参过程中我们确定了如下原则：\par
        1、训练轮数并非越大越好，当训练轮数过大时，WMAPE反而单增，这可能是过拟合导致的。\par
        2、学习率设置在较低值、子树深度设置的更低，可以获得较好的WMAPE值。\par
        即设置使得模型更保守的超参数，易于获得更好的训练结果。基于此，我们经多次参数调整、结果比较后，确定训练轮数为60，其他超参数如下：

        \begin{python}
            params={'booster':'gbtree',#可选：线性gblinear、树dart
            'eta':0.02,#学习率，0-1之间，增大该值使算法更激进
            'max_depth':10,#树的最大深度，增大该值使算法更复杂，但易过拟合
            'subsample':0.99,#子样本比率，增大该值可以预防过拟合
            'colsample_bytree':0.85,#构建每棵树时列的子样本比率
        }
        \end{python}

        \subsection*{效率与信度}
        XGBoost算法效率较高，训练轮数设为60，子树深度设为10时，约40秒就完成了从数据处理、训练模型到输出预测结果的全部过程。\par
        XGBoost的训练效果也较好，调参得到最好的WMAPE评价值为0.655，高于prophet算法所能达到的最好效果。  

    \subsection*{另外，我们还用LSTM模型进行了预测，但是由于数据量太大，运行时电脑险些出了故障，因此放弃，不过程序是可以运行的（数据量较少情况下），如果助教感兴趣可以试着运行一下！文件名$LSTM.py$}
        

        
       
\end{document}